﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Linq;
using System.Text;
using DotNetX;

namespace RPGXEngine
{
    public class ArrayVariableType : IVariableType
    {
        public IVariableType ArrayOrdinal
        {
            get { return _arrayOrdinal; }
            set { _arrayOrdinal = value; }
        }
        private IVariableType _arrayOrdinal = null;

        #region IVariableType Members

        public DataType Type
        {
            get { return DataType.Array; }
        }

        public string Name
        {
            get
            {
                return this._name;
            }
        }
        private string _name = String.Empty;

        public int SizeInBytes
        {
            get
            {
                int size = 0;

                foreach (var v in (List<IVariableType>)this.Value)
                {
                    size += v.SizeInBytes;
                }

                // TODO add this variables overhead etc...

                return size;
            }
        }

        public Dictionary<string, IVariableType> MetaData
        {
            get { return this._metaData; }
        }
        private Dictionary<string, IVariableType> _metaData = new Dictionary<string, IVariableType>();

        public object Value
        {
            get
            {
                object o = (object)0;

                if (this.ArrayOrdinal == null)
                {
                    o = _value;
                }
                else
                {
                    if (this.ArrayOrdinal.Value.IsInt32())
                    {
                        int idx = Convert.ToInt32(this.ArrayOrdinal.Value);

                        if (idx >= 0 && idx < this._value.Count)
                        {
                            IVariableType vt = this._value[idx];
                            if (vt is ArrayVariableType)
                                o = ((ArrayVariableType)vt)._value;
                            else
                                o = this._value[idx].Value;
                        }
                    }
                    else if (this.ArrayOrdinal.Value.IsString())
                    {
                        string key = this.ArrayOrdinal.Value.ToString();

                        if (this.MetaData.ContainsKey(key))
                        {
                            o = this.MetaData[key].Value;
                        }
                    }
                }

                return o;
            }
            set
            {
                if (this.ArrayOrdinal == null)
                {
                    _value = ((object)value as List<IVariableType>);
                }
                else
                {
                    if (this.ArrayOrdinal.Value.IsInt32())
                    {
                        int idx = Convert.ToInt32(this.ArrayOrdinal.Value);

                        var v = value as List<IVariableType>;
                        if (v == null)
                        {
                            IVariableType var = value as IVariableType;
                            if (var != null &&
                                idx >= 0 && idx < this._value.Count)
                            {
                                this._value[idx].SetValue(var);
                            }
                        }
                        else
                        {
                            if (idx >= 0 && idx < this._value.Count)
                            {
                                this._value[idx].SetValue(v[idx]);
                            }
                        }
                    }
                    else if(this.ArrayOrdinal.Value.IsString())
                    {
                        string key = this.ArrayOrdinal.Value.ToString();

                        if (!this.MetaData.ContainsKey(key))
                        {
                            this.MetaData.Add(key, null);
                        }

                        this.MetaData[key] = value as IVariableType;
                    }

                    // having an array or a boolean as an ordinal is not supported
                }
            }
        }
        private List<IVariableType> _value = new List<IVariableType>();

        #endregion

        #region Public Methods

        public ArrayVariableType(string name)
        {
            this._name = name;
        }

        public void SetValue(object value)
        {
        }

        public void SetValue(IVariableType value)
        {
            if (this.ArrayOrdinal == null)
            {
                ArrayVariableType v = value as ArrayVariableType;
                if (v != null)
                {
                    this._value.Clear();

                    this._value = v._value; // to get around the object data type of Value
                }
            }
            else
            {
                this.Value = value;
            }

            foreach (var key in value.MetaData.Keys)
            {
                if (!this.MetaData.ContainsKey(key))
                    this.MetaData.Add(key, null);

                this.MetaData[key] = value.MetaData[key];
            }
        }

        public void SetOrdinalValue(IVariableType ordinal1, IVariableType ordinal2)
        {
            if (ordinal1.Value.IsInt32() && ordinal2.Value.IsInt32())
            {
                int i1 = (int)ordinal1.Value;
                int i2 = (int)ordinal2.Value;

                if (i1 < this._value.Count && i2 < this._value.Count &&
                    i1 >= 0 && i2 >= 0)
                {
                    this._value[i1] = this._value[i2];
                }
            }
            else if (ordinal1.Value.IsInt32() && ordinal2.Value.IsString())
            {
                int i1 = (int)ordinal1.Value;
                string s1 = (string)ordinal2.Value;

                if (i1 < this._value.Count && i1 >= 0 &&
                    this.MetaData.ContainsKey(s1))
                {
                    this._value[i1] = this.MetaData[s1];
                }
            }
            else if (ordinal1.Value.IsString() && ordinal2.Value.IsInt32())
            {
                string s1 = (string)ordinal1.Value;
                int i1 = (int)ordinal2.Value;

                if (!this.MetaData.ContainsKey(s1))
                    this.MetaData.Add(s1, null);

                if (i1 < this._value.Count && i1 >= 0 &&
                    this.MetaData.ContainsKey(s1))
                {
                    this.MetaData[s1] = this._value[i1];
                }
            }
            else if (ordinal1.Value.IsString() && ordinal2.Value.IsString())
            {
                string s1 = (string)ordinal1.Value;
                string s2 = (string)ordinal2.Value;

                if (!this.MetaData.ContainsKey(s1))
                    this.MetaData.Add(s1, null);

                if (this.MetaData.ContainsKey(s1) &&
                    this.MetaData.ContainsKey(s2))
                {
                    this.MetaData[s1] = this.MetaData[s2];
                }
            }
        }

        public void AddValue(object value)
        {
        }

        public void AddValue(IVariableType value)
        {
        }

        public void MinusValue(object value)
        {
        }

        public void MinusValue(IVariableType value)
        {
        }

        public void MultiplyValue(object value)
        {
        }

        public void MultiplyValue(IVariableType value)
        {
        }

        public void DivideValue(object value)
        {
        }

        public void DivideValue(IVariableType value)
        {
        }

        #endregion
    }
}
